import { type Ref, ref } from 'vue'
import { type WorkBook, read, utils } from 'xlsx'
import { createEventHook, useFileDialog } from '@vueuse/core'
import { FileAccept } from '@/constants/file-accept'

/**
 * 导入 Excel 文件并解析
 * @type T 解析后的 excel 原始数据类型
 * @type R 应用 mapper 映射后的数据类型
 * @param config.mapper 用于转换解析后的数据
 * @param config.sheetFilter 用于筛选需要解析的 sheet
 */
export function useExcel<T, R = T>(config?: {
  mapper: (v: T) => R
  sheetFilter?: (sheetNames: string[]) => string[] | Promise<string[]>
}) {
  const { open, onChange } = useFileDialog({
    accept: FileAccept.Excel,
    reset: true,
    multiple: false,
    directory: false
  })

  const excelList: Ref<R[]> = ref([])
  const parse = createEventHook<R[]>()

  const sheetFilter = config?.sheetFilter ?? ((sheetNames: string[]) => sheetNames)
  const parseSheet = async (workBook: WorkBook): Promise<R[]> => {
    excelList.value = sheetToArray<T, R>(
      workBook,
      workBook.SheetNames.length > 1 ? await sheetFilter(workBook.SheetNames) : workBook.SheetNames,
      config?.mapper
    )

    parse.trigger(excelList.value)
    return excelList.value
  }

  onChange(async (fileList: FileList | null) => {
    if (fileList === null || fileList.item(0) === null) return

    const workBook = await transformFileToWorkBook(fileList.item(0)!)
    parseSheet(workBook)
  })

  return {
    excelList,
    openExcel: () => open(),
    onParsed: parse.on
  }
}

async function transformFileToWorkBook(file: File): Promise<WorkBook> {
  const buffer = await file.arrayBuffer()
  return read(buffer)
}

function sheetToArray<T, R = T>(workBook: WorkBook, includeSheets: string[], mapper?: (v: T) => R) {
  return includeSheets
    .filter((name) => workBook.SheetNames.includes(name)) // includeSheets 可能包含不存在的 sheet
    .reduce<R[]>((list, name) => {
      const sheetList = utils.sheet_to_json<T>(workBook.Sheets[name])
      return list.concat(...((mapper === undefined ? sheetList : sheetList.map(mapper)) as R[]))
    }, [])
}
